package oktx.config;

import oktx.TransactionContext;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import javax.transaction.TransactionManager;

/**
 * Created by dave on 18-5-18 下午9:03.
 */
public class SpringTransactionInterceptor extends TransactionInterceptor {
    private static final Logger LOG = LoggerFactory.getLogger(SpringTransactionInterceptor.class);
    private LocalTxTerminator terminator;
    private TransactionManager tm;

    public SpringTransactionInterceptor(BeanFactory beanFactory, TransactionAttributeSource tas) {
        super(null, tas);
        setBeanFactory(beanFactory);
    }

    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        terminator = getBeanFactory().getBean(LocalTxTerminator.class);
        tm = getBeanFactory().getBean(TransactionManager.class);
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        return invokeWithinTransaction(invocation.getMethod(), targetClass, () -> {
            if (tm != null && tm.getTransaction() != null) { // 仅处理JTA事务
                TransactionContext tc = TransactionContext.getContext();
                if (tc == null) {
                    tc = TransactionContext.newContext(tm.getTransaction()); // 根事务
                    tc.setClazz(targetClass);
                    tc.setMethod(invocation.getMethod().getName());
                    LOG.debug("开启事务：" + tc);
                } else {
                    tc.setDepth(tc.getDepth() + 1);
                    LOG.debug("逻辑事务：" + tc);

                    TransactionInfo ti = currentTransactionInfo();
                    if (ti.getTransactionStatus().isNewTransaction()) {
                        // TODO 把当前事务做成队列来处理
                        throw new RuntimeException("不支持的事务类型：" + Propagation.REQUIRES_NEW);
                    }
                }
            }

            return invocation.proceed();
        });
    }

    @Override
    protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
        TransactionContext tc = TransactionContext.getContext();
        int depth = tc.getDepth();
        if (depth == 1) { // 只有为根事务时可以到这里，远程托管的子事务只能到达2
            tc.setDepth(0);
            terminator.commit(tc.getXid(), true);
            LOG.debug("提交事务：" + tc);
        } else {
            tc.setDepth(depth - 1);
            LOG.debug("提交逻辑事务：" + tc);
        }
    }

    @Override
    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        TransactionContext tc = TransactionContext.getContext();
        int depth = tc.getDepth();
        if (depth == 1) {
            tc.setDepth(0);
            terminator.rollback(tc.getXid(), true);
            LOG.debug("回滚事务：" + tc);
        } else {
            tc.setDepth(depth - 1);
            LOG.debug("回滚逻辑事务：" + tc);
        }
    }

}
